/**
 * @file Utilities.c
 * @brief Routines that perform low-level operations having to do
 *  with display or geometry
 * @author Matt Stallmann @date 29 Dec 2001
 * $Id: Utilities.c 10 2007-06-28 20:08:57Z mfms $
 */

/*
 *  Copyright (C) 1989, 2001, 2007  Matthias Stallmann, Prashant Hebbar.
 *  Contact: matt_stallmann@ncsu.edu
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program (file COPYING.txt); if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
 *  USA.
 */

#include "All.h"

#define ERR_MSG_SIZE 1000
char err_msg[ERR_MSG_SIZE];

const char *cp_;

/** 
    Checks to see if the cursor is currently inside a vertex.
   
    INPUT:
    k = vx id,
    x, y = current cursor co-ord.
     
    OUTPUT:
    returns 1 for true, 0 else.
*/
int near(int k, int x, int y)
{
  if ((abs(x - XCORD(k)) < VX_R) && (abs(y - YCORD(k)) < VX_R))
    return(TRUE);
  return(FALSE);
} /* near() ends */

/** 
    Gets the vertex id of a vertex if present at the specified 
    co-ords. Returns negative value if no vertex present.
    
    INPUT:
    x, y - the "queried" co-ordinates.
    
    OUTPUT:
    A vx id, if present at the co-ord, -1 else.
*/
int get_vx_id(int x, int y)
{
  int i;

  for (i=0;i<number_of_vertices;i++) 
    if (near(i,x,y) && !DEL(i))
      return(i);
  return(NULL_VERTEX);

} /* get_vx_id() ends */

/** vertex 'id' is highlighted on the screen */
void HILITE(vertex id)
{
  XFillArc(disp, main_pixmap, rubber_gc,
           XCORD(id) - VX_R + VX_B, YCORD(id) - VX_R + VX_B,
           2 * (VX_R - VX_B), 2 * (VX_R - VX_B), 0, 64 * 360);
  XFillArc(disp, main_canvas, rubber_gc,
           XCORD(id) - VX_R + VX_B, YCORD(id) - VX_R + VX_B,
           2 * (VX_R - VX_B), 2 * (VX_R - VX_B), 0, 64 * 360);
}

/** display the vertex on the canvas */
void put_vx(vertex id)
{
  XFillArc(disp, main_pixmap, rubber_gc, XCORD(id) - VX_R, YCORD(id) - VX_R,
           2 * VX_R, 2 * VX_R, 0,
           64 * 360);
  XFillArc(disp, main_canvas, rubber_gc, XCORD(id) - VX_R, YCORD(id) - VX_R,
           2 * VX_R, 2 * VX_R, 0,
           64 * 360);
  ident(id, XCORD(id), YCORD(id));
  if (V_DB(id) & EXP_MSK)
    XMapRaised(disp,VWIN(id));
  if (V_DB(id) & H_L_MSK)
    HILITE(id);
  
  return;
} /* put_vx ends */

/**
   Labels a vertex with a numerical id.
   
   INPUT:
   id = the vertex id,
   x, y - the vertex co-ordinates.
*/
void ident(int id, int x, int y)
{
  char   num[3];
  
  num[2] = '\0';
  if (id > 99) id = id % 100; /* truncate id if too long */
  if (id < 10) {
    num[0] = (int)'0' + id; 
    num[1] = '\0';
    DRAW_STR(main_pixmap, x - CH_W/2, y + CH_H / 2, num);
    DRAW_STR(main_canvas, x - CH_W/2, y + CH_H / 2, num);
  }
  else {
    num[0] = (int)'0' + (int)(id/10);
    num[1] = (int)'0' + (int)(id%10);
    DRAW_STR(main_pixmap, x - CH_W, y + CH_H / 2, num);
    DRAW_STR(main_canvas, x - CH_W, y + CH_H / 2, num);
  }
  
  return;
  
} /* ident ends */

/** mov_elab_window(e,x,y)  moves the label window associated with edge
   e to a position centered at (x,y) */
void mov_elab_window(edge e, int x, int y)
{
  int width;         /* width of label window */

  /* should have a macro ELAB_WIDTH for this */
  width = XTextWidth(elab_font_struct,LABEL(e),strlen(LABEL(e))) +
    2 * ELAB_HPAD;

  XMoveWindow(disp, LWIN(e), LX(e) - width/2, LY(e) - elab_height/2);
}

/** 
    Moves a specified vertex along with its incident edges
    to a new position. Labelled edges are taken care of.
    
    INPUT:
    id = the vertex id of the vertex to be moved,
    x, y = the  new coordinates of this vertex.

    FUNCTIONS CALLED:
    adjust_draw().

    MODIFIED: 6-21-91 When vertex with self-loop is moved,
    position of self-loop stays fixed relative to
    vertex.
*/
void move_vx_ej(vertex id, int nu_x, int nu_y)
{
  int    idc, x_old, y_old;
  int    dif_x, dif_y;
  edge   l_pt, lp;
  
  /* erase the old vertex by redrawing */
  put_vx(id);
  /* erase the incident edges */
  l_pt = lp = V_FIRST(id);
  while (l_pt != NULL_EDGE) {
    put_edge(l_pt);
    l_pt = NXT(l_pt);
  }
  /* get the old vertex co-ords. */
  x_old = XCORD(id);
  y_old = YCORD(id);
  /* assign the new co-ords */
  XCORD(id) = nu_x; YCORD(id) = nu_y;
  /* update the vertex window position */ 
  VLX(id) = nu_x; VLY(id) = nu_y;
  XMoveWindow(disp,VWIN(id),nu_x,nu_y);
  /* draw the new vertex */
  put_vx(id);
  /* draw the new incident edges */
  l_pt = lp;
  while (l_pt != NULL_EDGE) {
    idc = other_vertex(id,l_pt);
    if (id!=idc) { /* normal edge: transform knot points and
                      labels */
      t_affine(XCORD(idc),YCORD(idc),x_old,y_old,nu_x,nu_y,
               &(KN(l_pt,0,0)),&(KN(l_pt,0,1)));
      t_affine(XCORD(idc),YCORD(idc),x_old,y_old,nu_x,nu_y,
               &(KN(l_pt,1,0)),&(KN(l_pt,1,1)));
      t_affine(XCORD(idc),YCORD(idc),x_old,y_old,nu_x,nu_y,
               &LX(l_pt),&LY(l_pt));
    }
    else {  /* self-loop: keep same relative position of
               knot points and labels */
      /* store the old knot vector */
      dif_x = nu_x - x_old;
      dif_y = nu_y - y_old;
      
      KN(l_pt,0,0) += dif_x;
      KN(l_pt,0,1) += dif_y;
      KN(l_pt,1,0) += dif_x;
      KN(l_pt,1,1) += dif_y;
      LX(l_pt) += dif_x;
      LY(l_pt) += dif_y;
    }
    put_edge(l_pt);
    /* move label window to new (centered) position (MS 3-2-92) */
    mov_elab_window(l_pt,LX(l_pt),LY(l_pt));
    l_pt = NXT(l_pt);
  }
} /* move_vx_ej ends */

/** 
      Draws an edge connecting the specified vertices. Takes care
      of all the edge attributes - label exposed, highlighted.

      INPUT:
      pt = the edge.

      FUNCTIONS CALLED:  draw_edge().
*/
void put_edge(edge pt)
{
  if( ! IS_VALID_EJ( pt ) ) {
    fprintf(stderr,"\nError (put_edge): bad edge pointer\n");
    abort();
  }
  if (!(E_DB(pt) & DSP_MSK)) return; /* no action if edge is not being
                                        displayed */
  /* draw the edge (highlighted or not, depending on flag */
  if (E_DB(pt) & H_L_MSK)
    draw_edge( hilite, pt );
  else
    draw_edge( rubber_gc,pt );
  
  /* Raise the label window if expose bit is set*/
  if (E_DB(pt) & EXP_MSK)
    XMapRaised(disp,LWIN(pt));
} /* put_edge ends */

void erase_edge(edge e)
     /* PRE: e is a valid edge pointer
        POST: edge e is erased from the display */
{
  if( ! IS_VALID_EJ( e ) ) {
    fprintf(stderr,"\nError (erase_edge): bad edge pointer\n");
    abort();
  }
  if (!(E_DB(e) & DSP_MSK)) return;
  /* ASSERT: e is a valid edge and it is not currently displayed */

  if (E_DB(e) & EXP_MSK) XUnmapWindow(disp,LWIN(e));
  /* ASSERT: edge label does not appear on the display */
 
  if (E_DB(e) & H_L_MSK)
    draw_edge( hilite, e );
  else
    draw_edge( rubber_gc, e );
  /* ASSERT: line segments of the edge do not appear on the display */
}

/**
   Draws the edge 'edj' with the GC 'l_gc'

   FUNCTIONS CALLED: calc_adjust(), get_slope().
*/
void draw_edge(GC l_gc, edge edj)
{
  int x1, y1, x2, y2, gx, gy;
  double theta;
  XPoint pnts[4];
  
  /* if both knots are close to the straight line between the
     vertices, then simply draw a straight line; otherwise there are
     three segments connecting 4 points */
  if(VX_1(edj) != VX_2(edj)
     && near_segment(KN(edj,0,0),KN(edj,0,1),
                     XCORD(VX_1(edj)),YCORD(VX_1(edj)),
                     XCORD(VX_2(edj)),YCORD(VX_2(edj)))
     && near_segment(KN(edj,1,0),KN(edj,1,1),
                     XCORD(VX_1(edj)),YCORD(VX_1(edj)),
                     XCORD(VX_2(edj)),YCORD(VX_2(edj)))) {
    x1 = XCORD(VX_1(edj));  y1 = YCORD(VX_1(edj));
    x2 = XCORD(VX_2(edj));  y2 = YCORD(VX_2(edj));
    calc_adjust(&x1,&y1,&x2,&y2,&gx,&gy);
    XDrawLine(disp, main_pixmap, l_gc, x1, y1, x2, y2);
    XDrawLine(disp, main_canvas, l_gc, x1, y1, x2, y2);
  }
  else {
    /* The calc. needs to be done at one go and the lines
       drawn together so that there is no apparent delay in
       drawing the edges */
    x1 = XCORD(VX_1(edj));  y1 = YCORD(VX_1(edj));
    x2 = KN(edj,0,0);    y2 = KN(edj,0,1);
    /* XPoint has Shorts in it */
    pnts[1].x = (short)x2;  pnts[1].y = (short)y2;
    calc_adjust(&x1,&y1,&x2,&y2,&gx,&gy);
    pnts[0].x = (short)x1;
    pnts[0].y = (short)y1;
    
    x1 = KN(edj,1,0);   y1 = KN(edj,1,1);
    x2 = XCORD(VX_2(edj)); y2 = YCORD(VX_2(edj));
    /* XPoint has Shorts in it */
    pnts[2].x = (short)x1;  pnts[2].y = (short)y1;
    calc_adjust(&x1,&y1,&x2,&y2,&gx,&gy);
    pnts[3].x = (short)x2; 
    pnts[3].y = (short)y2;
    /* Draw all the edges together. */
    XDrawLines(disp, main_pixmap, l_gc, pnts, 4, CoordModeOrigin);
    XDrawLines(disp, main_canvas, l_gc, pnts, 4, CoordModeOrigin);
  }
  
  if ( direct ) {
    theta = atan(get_slope(KN(edj,1,0),KN(edj,1,1),XCORD(VX_2(edj)),
                           YCORD(VX_2(edj))));
    if ( l_gc == rubber_gc )
      draw_arrow( x2, y2, theta, gx, gy, ARROW_LENGTH );
    else draw_arrow( x2, y2, theta, gx, gy, ARROW_LENGTH * ARROW_EXPANSION );
  }
} /* draw_edge ends */

/** returns the edge for which lwin is the label window  (or
   NULL_EDGE if none exists) */
edge edge_from_window(Window lwin)
{
  edge e;
  
  for_all_edges(e) {
    if (LWIN(e) == lwin) return e;
  }
  return(NULL_EDGE);
} /* edge_from_window ends */

/** returns vertex for which vwin is the label window (or
   NULL_VERTEX if none exists */
vertex vertex_from_window(Window vwin)
{
  vertex v;
  for_all_vertices(v) {
    if (VWIN(v) == vwin) return v;
  }
  return (NULL_VERTEX);
}

static int near_knot(int x, int y, edge e, int i)
     /* TRUE iff (x,y) is within epsilon of the i-th knot of edge e */
{
  return (abs(x-KN(e,i,0))<EPS)&&(abs(y-KN(e,i,1))<EPS);
}

/**
   Returns the edge with a knot point at (x,y), if any, and *aindx
   is initialized to be the index of the knot.

   OUTPUT : NULL_EDGE on error.

   FUNCTIONS CALLED : first_edge(), next_edge().
*/
edge get_knot_at(int x, int y, int * a_indx)
{
  edge   pt;
  char   more=TRUE;

  /* set pt to the first in the list of all edges */
  pt = first_edge();
  /* search the list */
  while ( pt != NULL_EDGE && more ) {
    /* is (x,y) near the knot_0 ? */
    if (near_knot(x,y,pt,0)) {
      more = FALSE;
      *a_indx = 0;
    }
    /* how about knot_1 ?? */
    else if (near_knot(x,y,pt,1)) {
      more = FALSE;
      *a_indx = 1;
    }
    else
      pt = next_edge(pt);
  } /* while not null */
  return( pt );
} /* get_knot_at() ends */

void draw_arrow(int xh, int yh, double theta, int gx, int gy,
                double length_factor)
{
  int ax1, ay1, ax2, ay2;       /* coordinates of arrow endpoints */
  
  theta = fabs(theta);
  ax1 = xh + gx * length_factor * cos( theta - ARROW_ANGLE );
  ay1 = yh + gy * length_factor * sin( theta - ARROW_ANGLE );
  ax2 = xh + gx * length_factor * cos( theta + ARROW_ANGLE );
  ay2 = yh + gy * length_factor * sin( theta + ARROW_ANGLE );
  XDrawLine(disp, main_pixmap, rubber_gc, ax1, ay1, xh, yh);
  XDrawLine(disp, main_canvas, rubber_gc, ax1, ay1, xh, yh);
  XDrawLine(disp, main_pixmap, rubber_gc, ax2, ay2, xh, yh);
  XDrawLine(disp, main_canvas, rubber_gc, ax2, ay2, xh, yh);
}/* draw_arrow ends */

/** Sets up a label window;
    modified 3-2-92 (MS) so that labels are centered */
void create_lab_win(Window p_win, EJ_NODE * edge_ptr)
{
  XSetWindowAttributes  attr_str;
  
  int lab_x,lab_y;            /* label coordinates */
  unsigned int lab_w,lab_h;      /* label dimensions */
  unsigned long   valuemask;
  
  lab_x = edge_ptr->my_label_x;
  lab_y = edge_ptr->my_label_y;
  lab_w = XTextWidth(elab_font_struct, edge_ptr->my_label,
                     strlen(edge_ptr->my_label)) + 2 * ELAB_HPAD;
  lab_h = elab_height;
  edge_ptr->my_label_window =
    XCreateSimpleWindow(disp, p_win,
                        lab_x - lab_w / 2, lab_y - lab_h / 2,
                        lab_w, lab_h,
                        ELAB_BORDER_WIDTH, black, white);
  attr_str.bit_gravity = NorthWestGravity;
  valuemask = CWBitGravity;
  XChangeWindowAttributes(disp, edge_ptr->my_label_window, valuemask,
                          &attr_str);
  
  XSelectInput(disp, edge_ptr->my_label_window, OwnerGrabButtonMask |
               ButtonPressMask | ButtonReleaseMask | KeyPressMask |
               ExposureMask | EnterWindowMask | LeaveWindowMask);
  XDefineCursor(disp, edge_ptr->my_label_window, label_curs);
} /* create_lab_win ends */

/** redraw the label for e (called when label changes or becomes
   exposed */
void redraw_edge_label(edge e)
{
  XClearWindow(disp, LWIN(e));
  XDrawString(disp, LWIN(e), elab_gc, ELAB_HPAD, elab_yoffset, LABEL(e),
              strlen(LABEL(e)));
  
  XFlush(disp);
}

/**
   Creates the vertex label window in parent p_win  for vx.
*/
void create_vlab_win(Window p_win, vertex vx)
{
  XSetWindowAttributes  attr_str;
  unsigned long      valuemask;
  int char_height;
  
  char_height = vlab_height;
  VWIN(vx)= XCreateSimpleWindow(disp, p_win, VLX(vx), VLY(vx),
                                XTextWidth(vlab_font_struct, VLABEL(vx),
                                           strlen(VLABEL(vx)))
                                + 2 * VLAB_HPAD, char_height,
                                VLAB_BORDER_WIDTH, black, white);
  attr_str.bit_gravity = NorthWestGravity;
  valuemask = CWBitGravity;
  XChangeWindowAttributes(disp, VWIN(vx), valuemask, &attr_str);
  
  XSelectInput(disp, VWIN(vx), OwnerGrabButtonMask |
               ButtonPressMask | ButtonReleaseMask | KeyPressMask |
               ExposureMask | EnterWindowMask | LeaveWindowMask);
  XDefineCursor(disp, VWIN(vx), label_curs);
  return;
} /* create_vlab_win ends */

void redraw_vertex_label(vertex vx)
     /* redraw the label for vx (called when label changes or becomes
        exposed */
{
  XClearWindow(disp, VWIN(vx));
  XDrawString(disp, VWIN(vx), vlab_gc, VLAB_HPAD, vlab_yoffset, VLABEL(vx),
              strlen(VLABEL(vx)));
  
  XFlush(disp);
}

/**
   Returns the edge if any at (xc,yc).

   OUTPUT: NULL_EDGE if no edge exists at that position.

   FUNCTIONS CALLED: first_edge(), next_edge(), check_on_edge().
   -*/
edge get_ej_at_xy(int xc, int yc)
{
  char   found = FALSE;
  edge   t_pnt;
  
  t_pnt =  first_edge();
  while ((!found)&&(t_pnt!=NULL_EDGE)) {
    if (check_on_edge(t_pnt,xc,yc))
      found = TRUE;
    else
      t_pnt = next_edge(t_pnt);
  } /* while not found */ 
  
  return(t_pnt);
  
} /* get_ej_at_xy ends */

/** 
   Checks if x,y belong to the edge pt.
*/
int check_on_edge(edge pt, int x, int y)
{
  vertex v1, v2;
  
  v1 = VX_1(pt);
  v2 = VX_2(pt);
  
  /* are we near first segment? */
  if (near_segment(x,y,XCORD(v1),YCORD(v1),KN(pt,0,0),KN(pt,0,1)))
    return(TRUE);
  
  /* how about second segment (between the two knots)? */
  if (near_segment(x,y,KN(pt,0,0),KN(pt,0,1),KN(pt,1,0),KN(pt,1,1)))
    return(TRUE);
  
  /* how about the third segment? */
  if (near_segment(x,y,KN(pt,1,0),KN(pt,1,1),XCORD(v2),YCORD(v2)))
    return(TRUE);
  
  /* if none of the above, then we're not near the edge */
  return(FALSE);
}

/** near_segment: returns true if the point (x,y) is within EPS of the
   line segment from (x1,y1) to (x2,y2); uses manhatten distance for all
   calculations so that integer arithmetic can be used throughout */
int near_segment(int x, int y, int x1, int y1, int x2, int y2)
{
  int raw_distance;     /* distance from (x,y) to line (not scaled) */
  int x_scale, y_scale;    /* scale factors for horizontal and
                              vertical distance */
  
  /* first check to see if (x,y) is "approximately" in the orthogonal
     rectangle with (x1,y1) and (x2,y2) at the corners */
  
  if (!(((x >= x1 - EPS && x <= x2 + EPS)
         || (x >= x2 - EPS && x <= x1 + EPS))
        && ((y >= y1 - EPS && y <= y2 + EPS)
            || (y >= y2 - EPS && y <= y1 + EPS))))
    return(FALSE);
  
  /* then check if horizontal or vertical distance to the line formed by
     (x1,y1) and (x2,y2) is <= EPS */
  
  raw_distance = abs(y * (x1 - x2) - x * (y1 - y2) + x2 * y1 - x1 * y2);
  x_scale = abs(x1 - x2); 
  y_scale = abs(y1 - y2);
  
  if (x_scale > y_scale) {
    if (raw_distance / x_scale <= EPS) return(TRUE);
  }
  else {
    if (raw_distance / y_scale <= EPS) return(TRUE);
  }
  return(FALSE);
}

void clear_main_canvas(void)
     /* clears the main window (and the pixmap) */
{
  Window root;
  int x, y;
  unsigned int width, height;
  unsigned int bw;
  unsigned int depth;
  XClearWindow(disp, main_canvas);  /* clear the main window */
  XGetGeometry(disp, main_pixmap, &root, &x, &y, &width, &height,
               &bw, &depth);
  XFillRectangle(disp, main_pixmap, erase_gc, 0, 0, width, height);
  /* clear the main pixmap */
}

/**
   Draws the graph from the adjacency list structure after clearing
   the main window.

   FUNCTIONS CALLED: put_edge().
*/
void draw_gr(void)
{
  vertex i;
  edge   lpt;
  
  clear_main_canvas();
  
  for ( i = 0; i < number_of_vertices; i++ ) { 
    /* deleted vx */
    if ( DEL(i) )
      continue;
    /* draw the vertex i */
    put_vx(i);
    /* now the edges */
    lpt = V_FIRST(i);
    while ( lpt != NULL_EDGE ) {
      if ( IS_VALID_EJ(lpt) ) {
        if ( VX_1(lpt) == i ) {
          put_edge(lpt);
          XFlush(disp);
        }
      }
      lpt = LNK(lpt);
    } /* while not NULL */
  } /* for i */
} /* draw_gr ends */

/** does an affine transformation for drawing arrow segments */
void calc_adjust(int * xc1, int * yc1, int * xc2, int * yc2,
                 int * gx, int * gy)
{
  
  double theta;
  
  *gx = *gy = 1; /* used in draw_arrow() */
  
  if (abs(*xc1-*xc2)<3) {  /* parallel to Y axis */
    
    theta = asin(1.0);
    if (*yc1<=*yc2){
      *yc1 += VX_R;  /* adjust just the y co-ords */
      *yc2 -= VX_R;
    }
    else{
      *yc1 -= VX_R;
      *yc2 += VX_R;
    }
  }
  else {
    theta = atan(((double)(*yc2-*yc1))/(*xc2-*xc1));
    if (*xc1 < *xc2) {
      *xc1 += VX_R*cos(theta);
      *yc1 += VX_R*sin(theta);
      *xc2 -= VX_R*cos(theta);
      *yc2 -= VX_R*sin(theta);
    }
    else {
      *xc2 += VX_R*cos(theta);
      *yc2 += VX_R*sin(theta);
      *xc1 -= VX_R*cos(theta);
      *yc1 -= VX_R*sin(theta);
    }
  }
  
  if (*yc1 < *yc2)   /* used in draw_arrow() */
    *gy = -1;
  if (*xc1 < *xc2)
    *gx = -1;
  
  return;
  
} /* calc_adjust() ends */

/**
  Calculates the new coordinate for *ax,*ay after one of the vertices
  of an edge is moved from x_old,y_old to x_new,y_new.
  x_fix,y_fix are the fixed vertex coordinates.

  INPUT:
  - x_fix, y_fix = coordinates of fixed vertex. 
  - x_old, y_old = coordinates of the vertex to be moved. 
  - x_new, y_new = coordinates of the vertex after move.
  - ax, ay (coordinates of label or knot point -- passed by ref)

  OUTPUT: ax, ay

  MODIFICATIONS:

  6-21-91 Fixed this so that the clockwise order of
  multiple edges is preserved
  
  If v_old is the vector between fixed vertex and old position of
  moved vertex and v_old_lab is the corresponding vector for the old
  position of the label or knot, then v_old_lab can be decomposed
  into a shadow vector, with the same direction as v_old, and an offset
  vector, which is orthogonal to v_old.
  
  If v_new is the vector corresponding to v_old for the new
  position of the moved vertex, then the new shadow vector will
  have the same direction as v_new and the new offset vector will
  be orthogonal to v_new (in the same relative direction as the
  old offset vector). Length of the new shadow vector will be
  scaled according to the ratio of the lengths of v_new versus
  v_old, but the length of the offest vector stays fixed.
  
  (note: if the vector v is given by coordinates (x,y), then
  v + 90 deg is (-y,x))
*/
void t_affine(int x_fix, int y_fix, int x_old, int y_old, int x_new, int y_new,
              int *ax, int *ay)
{
  int x_lab, y_lab;     /* coordinates of (ax,ay) */
  double dist_old, dist_new;  /* squared distance between fixed vertex and
                                 moved vertex (old and new) */
  double x_old_rel, y_old_rel;   /* relative x and y for old position of
                                    moved vertex (wrt fixed vertex) */
  double x_lab_rel, y_lab_rel;   /* relative x and y for old label
                                    position */
  double x_new_rel, y_new_rel;   /* relative x and y for new position of
                                    moved vertex */
  double x_rel, y_rel;     /* relative x and y for new label
                              position */
  double shadow_dist, offset_dist; /* relevant distances */
  
  x_lab = *ax;
  y_lab = *ay;
  /* calculate the distances */  
  dist_old = DIST(x_old,y_old,x_fix,y_fix);
  dist_new = DIST(x_new,y_new,x_fix,y_fix);
  /* get the edge and label vectors wrt fixed vertex*/
  x_old_rel = x_old - x_fix;
  x_lab_rel = x_lab - x_fix;
  x_new_rel = x_new - x_fix;
  y_old_rel = y_old - y_fix;
  y_lab_rel = y_lab - y_fix;
  y_new_rel = y_new - y_fix;
  /* compute shadow distance and offset distance */
  shadow_dist =
    (x_lab_rel * x_old_rel + y_lab_rel * y_old_rel) / dist_old;
  offset_dist =
    (-x_lab_rel * y_old_rel + y_lab_rel * x_old_rel) / dist_old;
  /* compute new relative positions of labels:
     new shadow vector + new offset vector,
     where the new shadow vector is scaled by the new edge
     length, but the offset vector is not */
  x_rel = x_new_rel * shadow_dist / dist_old
    - y_new_rel * offset_dist / dist_new;
  y_rel = y_new_rel * shadow_dist / dist_old
    + x_new_rel * offset_dist / dist_new;
  
  /* store absolute position of new label (or knot) */
  *ax = (int)(0.5 + x_fix + x_rel);
  *ay = (int)(0.5 + y_fix + y_rel);
} /* t_affine ends */

/**
   Returns the slope for the pair (x1,y1):(x2,y2).

   OUTPUT : Returns DBL_MAX (#defined in float.h) if parallel to
      Y axis within 3 pixels.
*/
double get_slope(int x1, int y1, int x2, int y2)
{
  if (abs(x1-x2)<3)  /* parallel to Y axis */
    return( DBL_MAX );
  else
    return( ((double)(y2-y1))/(x2-x1) );
  
} /* get_slope ends */

/**
   Handles all events that are not the result of intentional user
   intervention, particularly the Expose and ConfigureNotify
   events; called from any part of GDR where events are being
   dequeued.
   
   returns TRUE if eve has been handled, FALSE otherwise */
int handle_background_event( XEvent eve )
{
  switch (eve.type) {
  case MappingNotify:
    XRefreshKeyboardMapping((XMappingEvent *)&eve);
    return TRUE;
  case Expose: {
    vertex v;
    edge   e;
    TWIN   w;
    if (eve.xexpose.window == main_canvas) {
      /* redraw exposed image from pixmap */
      int x = eve.xexpose.x;
      int y = eve.xexpose.y;
      int width = eve.xexpose.width;
      int height = eve.xexpose.height;
      XCopyArea(disp, main_pixmap, main_canvas, copy_gc, x, y,
                width, height, x, y);
      return TRUE;
    }
    else if ( is_panel_window(eve.xexpose.window) ) {
      draw_panel_window();
      return TRUE;
    }
    else if (eve.xexpose.window == resume_window) {
      XClearWindow(disp, resume_window);
      DRAW_STR(resume_window, HPAD, CH_H + VPAD, "RESUME ?");
      return TRUE;
    }
    else if ((v = vertex_from_window(eve.xexpose.window)) != NULL_VERTEX) {
      redraw_vertex_label(v);
      return TRUE;
    }
    else if ((e = edge_from_window(eve.xexpose.window)) != NULL_EDGE) {
      redraw_edge_label(e);
      return TRUE;
    }
    else if ( (w = find_text_window(eve.xexpose.window))
              != NULL_TEXT_WINDOW ) {
      redraw_text_window(w);
      return TRUE;
    }
    return FALSE;
  } /* end, case Expose */
  case ConfigureNotify:
    if (eve.xconfigure.window == main_window) {
      Window          root;
      unsigned int    depth, bw;
      unsigned int    wind_width = eve.xconfigure.width - PANEL_WIDTH;
      unsigned int    wind_height = eve.xconfigure.height - HELP_HEIGHT;
      unsigned int    pix_width, pix_height;
      int x, y;
      /* resize the main canvas to correspond to main window */
      XResizeWindow(disp, main_canvas, wind_width, wind_height);
      //XMapRaised(disp, main_canvas);
      /* change size of pixmap if window size is larger than current
         main pixmap */
      XGetGeometry(disp, main_pixmap, &root, &x, &y,
                   &pix_width, &pix_height, &bw, &depth);
      if (pix_width < wind_width
          || pix_height < wind_height) {
        /* new pixmap dimensions are the maximum dimensions so far */
        unsigned int new_width = wind_width > pix_width ?
          wind_width : pix_width;
        unsigned int new_height = wind_height > pix_height ?
          wind_height : pix_height;
        /* create a new pixmap and copy image to it */
        Pixmap new_pixmap = XCreatePixmap(disp, main_canvas,
                                          new_width, new_height,
                                          XDefaultDepth(disp,
                                                        XDefaultScreen(disp)));
        XFillRectangle(disp, new_pixmap, erase_gc, 0, 0, new_width,
                       new_height);
        XCopyArea(disp, main_pixmap, new_pixmap, copy_gc, 0, 0,
                  pix_width, pix_height, 0, 0);
        XFreePixmap(disp, main_pixmap);
        main_pixmap = new_pixmap;
      }
      XCopyArea(disp, main_pixmap, main_canvas, copy_gc, 0, 0,
                wind_width, wind_height, 0, 0);
      return TRUE;
    }
    return FALSE;

  default:
    return FALSE;
  } /* switch */
}

void audio_bell(int duration)
{
  XBell(disp, duration);
}

/*  [Last modified: 2007 06 28 at 19:35:19 GMT] */
